#include "ned_pool.h"

#include "nedmalloc/nedmalloc.c"

#include <algorithm>

namespace cpac
{

	namespace ned_details
	{
		const size_t pool_count = 14;

		void* pool_footprint = reinterpret_cast<void*>(0x44415645); 

		nedalloc::nedpool* pools[pool_count + 1]        = { 0 };
		nedalloc::nedpool* alignedPools[pool_count + 1] = { 0 };

		size_t pool_id_from_size(size_t in_requested_size)
		{
			size_t pool_id = 0;

			if (in_requested_size > 0)
			{
				if (in_requested_size <= 16)
				{
					pool_id = (in_requested_size - 1) >> 2;
				}
				else
				{
					pool_id = std::min<size_t>(((in_requested_size - 1) >> 4) + 3, pool_count);
				}
			}

			return pool_id;
		}

		void* alloc(size_t in_requested_size)
		{
			size_t pool_id = pool_id_from_size(in_requested_size);
			nedalloc::nedpool* pool(0);

			if(pool_id < pool_count)
			{
				if(pools[pool_id] == 0)
				{
					pools[pool_id] = nedalloc::nedcreatepool(0, 8);
					nedalloc::nedpsetvalue(pools[pool_id], pool_footprint);
				}
				pool = pools[pool_id];
			}

			return nedalloc::nedpmalloc(pool, in_requested_size);
		}

		void* alloc_aligned(size_t in_align, size_t in_requested_size)
		{
			size_t pool_id = pool_id_from_size(in_requested_size);
			nedalloc::nedpool* pool(0);

			if (pool_id < pool_count)
			{
				if (alignedPools[pool_id] == 0)
				{
					alignedPools[pool_id] = nedalloc::nedcreatepool(0, 8);
					nedalloc::nedpsetvalue(alignedPools[pool_id], pool_footprint);
				}

				pool = alignedPools[pool_id];
			}

			return nedalloc::nedpmemalign(pool, in_align, in_requested_size);
		}

		void free(void* in_memory)
		{
			if (in_memory)
			{
				nedalloc::nedpool* pool(0);

				// nedalloc lets us get the pool pointer from the memory pointer
				void* footprint = nedalloc::nedgetvalue(&pool, in_memory);

				// Check footprint
				if (footprint == pool_footprint)
				{
					// If we allocated the pool, deallocate from this pool...
					nedalloc::nedpfree(pool, in_memory);
				}
				else
				{
					// ...otherwise let nedalloc handle it.
					nedalloc::nedfree(in_memory);
				}
			}
		}
	};	// namespace ned_details

	void* ned_pool::alloc_bytes(size_t in_count, char const* in_file, int in_line, char const* in_func)
	{
		void* pointer = ned_details::alloc(in_count);

		return pointer;
	}

	void* ned_pool::alloc_bytes_aligned(size_t in_align, size_t in_count, char const* in_file, int in_line, char const* in_func)
	{
		void* pointer = in_align ? ned_details::alloc_aligned(in_align, in_count) : ned_details::alloc_aligned(16, in_count);

		return pointer;
	}

	void ned_pool::dealloc_bytes(void* in_pointer)
	{
		if(!in_pointer)
			return;

		ned_details::free(in_pointer);
	}

	void ned_pool::dealloc_bytes_aligned(size_t in_align, void* in_pointer)
	{
		if(!in_pointer)
			return;

		ned_details::free(in_pointer);
	}
};